package cn.apestar.common.exception;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BaseException extends RuntimeException
  implements Serializable
{
  protected final Logger logger = LoggerFactory.getLogger(getClass());
  private Exception exception;
  private Logger log;
  private TYPE exceptionType;
  private String userMessage = "";
  private String code = "0";
  private String errorMessage = "";
  private Object keyObject = null;
  private boolean isExistKeyObject = false;
  private String message = null;

  public BaseException(Exception exception, Logger log)
  {
    this.exception = exception;
    this.log = log;

    if ((exception instanceof BaseException)) {
      formatBaseException();
      writeTraceErrorLog();
    }
    else
    {
      writeSysErrorLog();
    }
  }

  public BaseException(Exception exception, Logger log, Object keyObject)
  {
    this.exception = exception;
    this.log = log;
    this.keyObject = keyObject;
    this.isExistKeyObject = true;

    if ((exception instanceof BaseException)) {
      formatBaseException();
      writeTraceErrorLog();
    }
    else
    {
      writeSysErrorLog();
    }
  }

  public BaseException(String code, Logger log)
  {
    this.code = code;
    this.log = log;
    writeAppErrorLog();
  }

  public BaseException(Exception exception, String code, Logger log)
  {
    this.exception = exception;
    this.code = code;
    this.log = log;
    if ((exception instanceof BaseException)) {
      formatBaseException();
      writeTraceErrorLog();
    }
    else {
      writeSysErrorLog();
    }
  }

  public BaseException(String code, Logger log, Object keyObject)
  {
    this.code = code;
    this.log = log;
    this.keyObject = keyObject;
    this.isExistKeyObject = true;
    writeAppErrorLog();
  }

  public BaseException(Exception exception, String code, Logger log, Object keyObject)
  {
    this.exception = exception;
    this.code = code;
    this.log = log;
    this.keyObject = keyObject;
    this.isExistKeyObject = true;
    if ((exception instanceof BaseException)) {
      formatBaseException();
      writeTraceErrorLog();
    }
    else {
      writeSysErrorLog();
    }
  }

  public BaseException(String message)
  {
    this.message = message;
    this.exceptionType = TYPE.BIZ;
  }
  
  public BaseException(String code,String message)
  {
    this.message = message;
    this.code = code;
    this.exceptionType = TYPE.BIZ;
  }

  private void formatBaseException()
  {
    this.message = this.exception.getMessage();
  }

  public void formatBaseException(Throwable t) {
    this.message = t.getMessage();
  }


 

  private void writeSysErrorLog()
  {
    try
    {
      String exceptionStackTraceInfo = getExceptionStackTraceInfo();

      StackTraceElement stackTraceElement = getStackTrace()[0];
      String errorInfo = "";
      if (this.isExistKeyObject) {
        errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "],关键数据[{}]";
        this.log.error(errorInfo + exceptionStackTraceInfo, this.keyObject);
      }
      else {
        errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "]";
          this.log.error(errorInfo + exceptionStackTraceInfo);
      }
    }
    catch (Exception e) {
      this.logger.error(e.getMessage());
    }
  }

  private void writeAppErrorLog()
  {
    try
    {
      StackTraceElement stackTraceElement = getStackTrace()[0];
      String errorInfo = "";
      if (this.isExistKeyObject) {
        errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "],关键数据[{}]";
        this.log.error(errorInfo, this.keyObject);
      }
      else {
        errorInfo = "方法[" + stackTraceElement.getMethodName() + "],代码[" + this.code + "],异常[" + this.errorMessage + "]";
        this.log.error(errorInfo);
      }
    }
    catch (Exception e) {
      this.logger.error(e.getMessage());
    }
  }

  private void writeTraceErrorLog()
  {
    try
    {
      StackTraceElement stackTraceElement = getStackTrace()[0];
      String errorInfo = "方法[" + stackTraceElement.getMethodName() + "]";
      this.log.error(errorInfo);
    }
    catch (Exception e) {
      this.logger.error(e.getMessage());
    }
  }

  public String getMessage()
  {
    if (this.message == null) {
      this.message = (this.userMessage + "[" + this.code + "],请与管理员联系");
    }
    return this.message;
  }

  public String getErrorCode()
  {
    return this.code;
  }

  public TYPE getExceptionType()
  {
    return this.exceptionType;
  }

  private String getExceptionStackTraceInfo()
  {
    String exceptionTrace = "";
    try {
      StringWriter sw = new StringWriter();
      PrintWriter pw = new PrintWriter(sw);
      this.exception.printStackTrace(pw);
      exceptionTrace = "\r\n" + sw.toString();
      pw.flush();
      pw.close();
      sw.flush();
      sw.close();
    }
    catch (Exception e) {
      this.logger.error(e.getMessage());
    }
    return exceptionTrace;
  }

  static enum TYPE
  {
    DATABASE, SYSTEM, APPLY, BIZ, RULE;
  }
}